/*
	Prototype is developed by Benato Denis.
	Prototype is a project started on 23/09/2013.
	Prototype is release under LGPLv3 license.
	If you use part of Prototype software on other
	projects, you must copy this comment into the source
	code of the other projects, and quote Benato Denis 
	in the credits of the other software.
*/

#include "Prototype.h"

#include "Program.h"
#include "Generator.h"

using namespace std;

int main(int argc, char *argv[])
{
	if ((argc <= 1) || ((argc >= 2) && ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0))))
	{
		printf("\nUsage: Prototype [OPTION] [FILE]\n");
		printf("Compile, Debug, Execute of Prototype programs. Whith no OPTION, same as -h\n\n");
		printf("  -e, --execute  Execute the program on FILE,\n");
		printf("  -i, --info     Display info about the program on FILE\n");
		printf("  -c, --compile  Compile the source on FILE\n");
		printf("  -r, --reverse  Reverse the program on FILE\n");
		printf("\n");
		printf("  -h, --help     Display this help and exit\n");
		printf("  -a, --about    Display information about Prototype and exit\n");
		printf("\n");
	}
	else
	{
		if ((strcmp(argv[1], "-a") == 0) || (strcmp(argv[1], "--about") == 0)) {
			struct PrototypeVersion version = LoadVersionDescriptor();
			printf("\nPrototype version %d.%d ", version.major, version.minor);
			switch (version.release)
			{
				case 0:
					printf("stable");
					break;
				case 1:
					printf("alpha");
					break;
				case 2:
					printf("beta");
					break;
				default:
					printf("Development");
					break;
			}
			printf("\nPrototype was written by Benato Denis\nProject started on 23/09/2013\n");
		} else if ((strcmp(argv[1], "-r") == 0) || (strcmp(argv[1], "--reverse") == 0)) {
			if (access(argv[2], R_OK) == 0)
			{
				FILE *newfile;
				newfile = fopen(argv[3], "w");
				fclose(newfile);
				if ((argc > 3) && (access(argv[3], W_OK) == 0))
				{
					switch (Decompile(argv[2], argv[3]))
					{
						case 1:
							printf("\nProgram reversing process was a success\n");
							break;
						default:
							printf("\nThe specified file cannot be analyzed by Prototype\n\n");
							break;
					}
				} else {
					printf("\nYou must specify a valid file that can be filled with the output or the reverse process\n\n");
					exit(EXIT_FAILURE);
				}
			} else {
				printf("\nThe specified file does not exist\n\n");
				exit(EXIT_FAILURE);
			}
		} else if ((strcmp(argv[1], "-e") == 0) || (strcmp(argv[1], "--execute") == 0)) {
			if (access(argv[2], R_OK) == 0)
			{
				FILE *pFile;
				pFile = fopen(argv[2], "rb");
				fseek(pFile, 0, SEEK_END);
				unsigned long pFileSize = (unsigned long)ftell(pFile);
				fclose(pFile);
				if ((pFileSize >= (sizeof(struct ProgramInfo) + sizeof(struct PrototypeVersion) + sizeof(struct Instruction))) && ((pFileSize - (sizeof(struct ProgramInfo) + sizeof(struct PrototypeVersion))) % sizeof(struct Instruction) == 0))
				{
					Program* CurrentProgram = new Program;
					CurrentProgram->Load(argv[2]);
					if (CurrentProgram->Executable())
					{
						do
						{
							CurrentProgram->ExecuteInstruction();
						} while (CurrentProgram->EndOfInstructions() != true);
						printf("\nExecution of the program finished\n\n");
					} else {
						printf("\nThis Prototype version is out of date\n\n");
						delete CurrentProgram;
						exit(EXIT_FAILURE);
					}
					delete CurrentProgram;
				} else {
					printf("\nThe specified file cannot be executed by Prototype\n\n");
					exit(EXIT_FAILURE);
				}
			} else {
				printf("\nThe specified file does not exist\n\n");
				exit(EXIT_FAILURE);
			}
		} else if ((strcmp(argv[1], "-i") == 0) || (strcmp(argv[1], "--info") == 0)) {
			if (access(argv[2], R_OK) == 0)
			{
				FILE *pFile;
				pFile = fopen(argv[2], "rb");
				fseek(pFile, 0, SEEK_END);
				unsigned long pFileSize = (unsigned long)ftell(pFile);
				fclose(pFile);
				if ((pFileSize >= (sizeof(struct ProgramInfo) + sizeof(struct PrototypeVersion) + sizeof(struct Instruction))) && ((pFileSize - (sizeof(struct ProgramInfo) + sizeof(struct PrototypeVersion))) % sizeof(struct Instruction) == 0))
				{
					Program* CurrentProgram = new Program;
					CurrentProgram->Load(argv[2]);
					struct ProgramInfo* info = CurrentProgram->GetProgramInfo();
					if (info == (struct ProgramInfo*)NULL)
					{
						printf("Program information not available\n\n");
						delete CurrentProgram;
						exit(EXIT_FAILURE);
					} else {
						printf("\nName: %s\nAuthor: %s\nDescription: %s\nLicense: ", info->name, info->author, info->description);
						switch (info->license)
						{
							case 0:
								printf("GNU General Public License (GPL v3)");
								break;
							case 1: 
								printf("GNU Lesser General Public License (LGPL v3)");
								break;
							case 2: 
								printf("GNU Affero General Public License (AGPL v3)");
								break;
							case 3:
								printf("GNU All-Permissive License");
								break;
							case 4:
								printf("FreeBSD License");
								break;
							case 5:
								printf("zlib License");
								break;
							default:
								printf("Undefined");
								break;
						}
						printf("\n\n");
						free(info);
						info = (struct ProgramInfo*)NULL;
						struct PrototypeVersion *version = CurrentProgram->GetCompilerInfo();
						printf("Compiled with Prototype version %d.%d ", version->major, version->minor);
						switch (version->release)
						{
							case 0:
								printf("stable");
								break;
							case 1:
								printf("alpha");
								break;
							case 2:
								printf("beta");
								break;
							default:
								printf("Development");
								break;
						}
						free(version);
						version = (struct PrototypeVersion*)NULL;
						printf("\n\n");
						if (CurrentProgram->Executable())
							printf("This version of Prototype can execute the specified program");
						else
							printf("This version of Prototype is unable to execute the specified program");
						printf("\n\n");
						unsigned long Instructions = CurrentProgram->GetInstructionsNumber();
						printf("Number of instructions: %d\n\n", Instructions);
					}
					delete CurrentProgram;
				} else {
					printf("\nThe specified file cannot be executed by Prototype\n\n");
					exit(EXIT_FAILURE);
				}
			} else {
				printf("\nThe specified file does not exist\n\n");
				exit(EXIT_FAILURE);
			}
		} else {
			printf("\nUsage: Prototype [OPTION] [FILE]\n");
			printf("Compile, Debug, Execute of Prototype programs. Whith no OPTION, same as -h\n\n");
			printf("  -e, --execute  Execute the program on FILE,\n");
			printf("  -i, --info     Display info about the program on FILE\n");
			printf("  -c, --compile  Compile the source on FILE\n");
			printf("  -r, --reverse  Reverse the program on FILE\n");
			printf("\n");
			printf("  -h, --help     Display this help and exit\n");
			printf("  -a, --about    Display information about Prototype and exit\n");
			printf("\n");
		}
	}
	return EXIT_SUCCESS;
}

struct PrototypeVersion LoadVersionDescriptor(void)
{
	struct PrototypeVersion CurrentVersion;
	CurrentVersion.major = MAJOR;
	CurrentVersion.minor = MINOR;
	CurrentVersion.release = RELEASE;
	return CurrentVersion;
}
